package com.df.my_net_game.net.base;

import com.df.my_net_game.net.tcp.EzGameTcpDefatultChannelInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;

@Data
@Slf4j
public class GTcpServer extends Thread {
    public Integer port;
    protected ServerBootstrap bootstrap;
    protected int maxContentLength = 65536;
    protected int readerIdleTimeSecond = 30;
    protected int writerIdleTimeSecond = 150;
    protected int allIdleTimeSecond = 200;
    private int msgType = 0;//0二进制 1字符串

    //协议
    //序列化

    /**
     * boss线程组
     */
    protected  NioEventLoopGroup bossGroup;
    /**
     * 工作线程组
     */
    protected  NioEventLoopGroup workGroup;

    protected BiConsumer<ChannelHandlerContext, IdleStateEvent> idleFunc;//读写空闲回调
    protected List<Supplier<ChannelHandler>> handlers = new ArrayList<>();//自定义的处理器
    protected ChannelInitializer channelInitializer;//初始化器
    protected IServerCB serverCB;//生命周期回调
    protected Map<ChannelOption,Object> bossOptions = new HashMap<ChannelOption,Object>();//接收连接的线程组
    protected Map<ChannelOption,Object> workOptions = new HashMap<ChannelOption,Object>();//处理消息的线程组
    public GTcpServer(){
        super("default");
    }



    @Override
    public void run() {
        this.runDo();
    }

    public void runDo(){
        prepare();
        try {
            ServerBootstrap server = this.bootstrap;
            //Netty 主从Reactor多线程模型
            //option()是提供给NioServerSocketChannel用来接收连接, 也就是Boss线程组,接受客户端的连接
            //childOption()是提供给ServerChanel接收到的连接, 也就是worker线程组-主要用来处理IO逻辑处理
            ServerBootstrap channel = server.group(this.bossGroup, this.workGroup)
                    .channel(NioServerSocketChannel.class);
            // handler
            addChannleHandler(channel);
            //Toptions
            addBossOptions2Channel(channel);
            addWorkOptions2Channel(channel);

            //绑定端口
            ChannelFuture future = server.bind(port);
            future.addListener((ChannelFutureListener) channelFuture -> {
                if (channelFuture.isSuccess()) {
                    // 成功回调
                    if(serverCB != null){
                        serverCB.onServerStartSuccess(channelFuture,this);
                    }
                    log.info(this.getName() + " Listen端口 " + port + " 成功");
                } else {
                    // 失败回调
                    if(serverCB != null){
                        serverCB.onServerStartFailure(channelFuture,this);
                    }
                    log.info(this.getName() + " Listen端口 " + port + " 失败");
                }
            });
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
            //优雅关机
            close();
        }
    }


    private void addChannleHandler(ServerBootstrap channel) {
        channel.childHandler(channelInitializer);
    }

    private void addWorkOptions2Channel(ServerBootstrap channel) {
        if(!workOptions.isEmpty()){
            for (ChannelOption channelOption : workOptions.keySet()) {
                channel.childOption(channelOption, workOptions.get(channelOption));
            }
        }else{
            //等待连接通信队列大小,服务器套接字的最大连接数,配置 ServerSocketChannel 的接受连接队列的大小
            channel.option(ChannelOption.SO_BACKLOG, 1024)
                    //发送接收缓冲器
                    .childOption(ChannelOption.SO_SNDBUF, 2048 * 1024)
                    .childOption(ChannelOption.SO_RCVBUF, 512 * 1024)
                    //TCP处理
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //重用地址
                    .childOption(ChannelOption.SO_REUSEADDR, true)
                    .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
                            new WriteBufferWaterMark(512 * 1024, 2048 * 1024));
        }

    }

    private void addBossOptions2Channel(ServerBootstrap channel) {
        if(!bossOptions.isEmpty()){
            for (ChannelOption channelOption : bossOptions.keySet()) {
                channel.option(channelOption, bossOptions.get(channelOption));
            }
        }else {
            //等待连接通信队列大小,服务器套接字的最大连接数,配置 ServerSocketChannel 的接受连接队列的大小
            channel.option(ChannelOption.SO_BACKLOG, 1024)
                    //buffer配置
                    .option(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator())
                    //池化处理
                    .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        }
    }

    public void close() {
        if(bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        if(workGroup != null) {
            workGroup.shutdownGracefully();
        }
        log.info(getName()+" closed!");
    }

    protected void prepare() {
        if(port==null){
            throw new RuntimeException(getName()+" 启动失败！未指定监听端口！");
        }
        if(channelInitializer==null){
//            throw new RuntimeException(getName()+" 启动失败！未设置channelInitializer");
            channelInitializer = new EzGameTcpDefatultChannelInitializer(this);
        }
        if(bootstrap==null){
            bootstrap = new ServerBootstrap();
        }
        if(bossGroup==null){
            bossGroup = new NioEventLoopGroup();
        }
        if(workGroup==null){
            workGroup = new NioEventLoopGroup();
        }
        if(serverCB==null){
            throw new RuntimeException(getName()+" 启动失败！未指定服务回调！");
        }
    }



    public GTcpServer setServerName(String name) {
        super.setName(name);
        return this;
    }
    public GTcpServer setMsgType(Integer type) {
        this.msgType = type;
        return this;
    }
    public GTcpServer setPort(Integer port) {
        this.port = port;
        return this;
    }
    public GTcpServer addWorkOptions(ChannelOption option, Object value) {
        bossOptions.put(option, value);
        return this;
    }
    public GTcpServer addBossOptions(ChannelOption option, Object value) {
        workOptions.put(option, value);
        return this;
    }
    public GTcpServer addHandler(Supplier<ChannelHandler> handler) {
        handlers.add(handler);
        return this;
    }
    public GTcpServer setSuccessFunc(Consumer<ChannelFutureListener> successFunc) {
        successFunc = successFunc;
        return this;
    }
    public GTcpServer setFailureFunc(Consumer<ChannelFutureListener> failureFunc) {
        failureFunc = failureFunc;
        return this;
    }
}
